package com.business.lgh.naocs.naming.core;

import com.business.lgh.nacos.api.common.Constants;
import com.business.lgh.nacos.api.naming.utils.NamingUtils;
import com.business.lgh.naocs.naming.consistency.*;
import com.business.lgh.naocs.naming.constant.Action;
import com.business.lgh.naocs.naming.core.pojo.ClusterWrapper;
import com.business.lgh.naocs.naming.core.pojo.InstanceWrapper;
import com.business.lgh.naocs.naming.core.pojo.Instances;
import com.business.lgh.naocs.naming.core.pojo.ServiceWrapper;
import com.business.lgh.naocs.naming.misc.Datum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author lgh
 * @version V1.0
 * @Package com.business.lgh.naocs.naming.core.pojo
 * @date 2020/6/24 5:00 下午
 */
@Slf4j
@Component
public class ServiceManager implements RecordListener<ServiceWrapper> {

    @Autowired
    ConsistencyService consistencyService;

    /**
     * 存放服务的集合
     */
    private Map<String, Map<String, ServiceWrapper>> serviceMap = new ConcurrentHashMap<>();

    /**
     * 用来上锁
     */
    private final Object putServiceLock = new Object();

    @Override
    public void onChange(String key, ServiceWrapper value) {

    }

    /**
     * 注册实例
     *
     * @param namespaceId
     * @param serviceName
     * @param instanceWrapper
     */
    public void registerInstance(String namespaceId, String serviceName, InstanceWrapper instanceWrapper) {
        //创建空服务，若已有则不会创建
        createEmptyService(namespaceId, serviceName, instanceWrapper.isEphemeral());
        //获取服务
        ServiceWrapper service = getService(namespaceId, serviceName);
        if (service == null) {
            log.error("service is register fail:#{}", serviceName);
            return;
        }
        addInstance(service, instanceWrapper);
    }

    /**
     * 注册实例
     *
     * @param namespaceId
     * @param serviceName
     * @param instanceWrapper
     */
    public void deRegisterInstance(String namespaceId, String serviceName, InstanceWrapper instanceWrapper) {
        //获取服务
        ServiceWrapper service = getService(namespaceId, serviceName);
        if (service == null) {
            log.error("service is register fail:#{}", serviceName);
            return;
        }
        synchronized (service) {
            removeInsance(service,instanceWrapper);
        }

    }

    private void removeInsance(ServiceWrapper service, InstanceWrapper instanceWrapper) {

        String key = KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), instanceWrapper.isEphemeral());
        List<InstanceWrapper> instanceList = updateIpAddresses(service, true, Action.REMOVE, instanceWrapper);

        //把列表包装成新的instance
        Instances instances = new Instances();
        instances.setInstances(instanceList);

        consistencyService.put(key, instances);
    }



    /**
     * 获取实例
     * @param namespaceId
     * @param serviceName
     * @param clusterName
     * @param ip
     * @param port
     * @return
     */
    public InstanceWrapper getInstance(String namespaceId, String serviceName, String clusterName, String ip, int port) {
        ServiceWrapper service = getService(namespaceId, serviceName);
        if (service == null) {
            return null;
        }
        List<InstanceWrapper> collect = service.getClusterMap().get(clusterName).getEphemeralInstances().stream()
                .filter(x -> x.getIp().equals(ip) && x.getPort() == port).collect(Collectors.toList());
        if (collect.size()==0){
            return null;
        }
        return collect.get(0);
    }

    public void restBeatInstance(String namespaceId, String serviceName, String clusterName, String ip, int port) {
        ServiceWrapper service = getService(namespaceId, serviceName);
        if (service == null) {
            return;
        }
        List<InstanceWrapper> collect = service.getClusterMap().get(clusterName).getEphemeralInstances().stream()
                .filter(x -> x.getIp().equals(ip) && x.getPort() == port).collect(Collectors.toList());
        if (collect.size()==0){
            return;
        }
        collect.get(0).setBeatTime(System.currentTimeMillis());
    }

    /**
     * 添加实例
     *
     * @param instanceWrapper
     */
    private void addInstance(ServiceWrapper service, InstanceWrapper instanceWrapper) {

        String key = KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), instanceWrapper.isEphemeral());
        synchronized (service) {
            //获取最新整合后的实例列表
            List<InstanceWrapper> instanceList = addIpAdrresses(service, instanceWrapper.isEphemeral(), instanceWrapper);

            //把列表包装成新的instance
            Instances instances = new Instances();
            instances.setInstances(instanceList);

            consistencyService.put(key, instances);
        }
    }

    private List<InstanceWrapper> addIpAdrresses(ServiceWrapper service, boolean ephemeral, InstanceWrapper instanceWrapper) {
        return updateIpAddresses(service, ephemeral, Action.ADD, instanceWrapper);
    }

    /**
     * 更新集合,这里还会取对已存在的实例更新
     *
     * @param service
     * @param ephemeral
     * @param instanceWrapper
     * @param add
     * @return
     */
    private List<InstanceWrapper> updateIpAddresses(ServiceWrapper service, boolean ephemeral, Action add, InstanceWrapper... instanceWrappers) {
        //这是主要的数据结构
        Datum datum = consistencyService.get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));
        List<InstanceWrapper> currentIps = service.allIps(ephemeral);

        //转换下数据结构为<ip,instance>
        Map<String, InstanceWrapper> currentInstances = currentIps.stream().
                collect(Collectors.toMap(InstanceWrapper::getIpAddress, Function.identity()));
        Map<String, InstanceWrapper> instanceMap;

        //若datum里有数据则以它为模板更新信息（心跳信息等），可以理解为  datum.list -> left join-> service.clustermap.cluster.list
        if (datum != null) {
            instanceMap = setValid(((Instances) datum.value).getInstances(), currentInstances);
        } else {
            instanceMap = new HashMap<>();
        }

        //目的是把新增的实例添加进去 datum.list
        for (InstanceWrapper instance : instanceWrappers) {
            if (!service.getClusterMap().containsKey(instance.getClusterName())) {
                ClusterWrapper clusterWrapper = new ClusterWrapper(instance.getClusterName(), service);
                clusterWrapper.init();
                service.getClusterMap().put(instance.getClusterName(), clusterWrapper);
            }

            if (add.equals(Action.ADD)) {
                instanceMap.put(instance.getDatumKey(), instance);
            }else if (add.equals(Action.REMOVE)){
                instanceMap.remove(instance.getDatumKey());
            }
        }

        return new ArrayList<>(instanceMap.values());
    }

    private Map<String, InstanceWrapper> setValid(List<InstanceWrapper> oldInstances, Map<String, InstanceWrapper> currentInstances) {
        //更新datastore里的数据
        Map<String, InstanceWrapper> instanceMap = oldInstances.stream().map(instance -> {
            InstanceWrapper instanceTmp;
            if ((instanceTmp = currentInstances.get(instance.getIp())) != null) {
                instance.setBeatTime(instanceTmp.getBeatTime());
                instance.setHealthy(instance.isHealthy());
            }
            return instance;
        }).collect(Collectors.toMap(InstanceWrapper::getDatumKey, Function.identity()));
        return instanceMap;
    }


    /**
     * 创建空服务
     *
     * @param namespaceId
     * @param serviceName
     * @param isLocal
     */
    public void createEmptyService(String namespaceId, String serviceName, boolean isLocal) {
        createServiceIfAbsent(namespaceId, serviceName, isLocal, null);
    }

    /**
     * 创建服务
     *
     * @param namespaceId
     * @param serviceName
     * @param isLocal
     * @param cluster
     */
    private void createServiceIfAbsent(String namespaceId, String serviceName, boolean isLocal, ClusterWrapper cluster) {
        ServiceWrapper service = getService(namespaceId, serviceName);
        if (service == null) {
            service = new ServiceWrapper();
            service.setName(serviceName);
            service.setNamespaceId(namespaceId);
            service.setGroupName(NamingUtils.getServiceName(serviceName));
            service.setLastModifyTime(System.currentTimeMillis());

            if (cluster != null) {
                cluster.setService(service);
                service.getClusterMap().put(cluster.getClusterName(), cluster);
            }
            //校验一下
            service.validate();
            putServiceAndInit(service);
        }

    }

    /**
     * 存入service并且实例化
     *
     * @param service
     */
    private void putServiceAndInit(ServiceWrapper service) {
        putService(service);
        service.init();

        //这里添加了两个监听，后续会用来作数据转换处理
        consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
        consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);

    }

    private void putService(ServiceWrapper service) {
        //判断是否包含该命名空间
        if (!serviceMap.containsKey(service.getNamespaceId())) {
            synchronized (putServiceLock) {
                //先存入命名空间
                if (!serviceMap.containsKey(service.getNamespaceId())) {
                    serviceMap.put(service.getNamespaceId(), new ConcurrentHashMap<>(16));
                }
            }
        }
        //再存入服务
        serviceMap.get(service.getNamespaceId()).put(service.getName(), service);
    }

    /**
     * 获取服务
     *
     * @param namespaceId
     * @param serviceName
     * @return
     */
    public ServiceWrapper getService(String namespaceId, String serviceName) {
        if (serviceMap.get(namespaceId) == null) {
            return null;
        }
        return serviceMap.get(namespaceId).get(serviceName);
    }

    public int getPagedService(String namespaceId, int startPage, int pageSize, String param, String containedInstance, List<ServiceWrapper> serviceList, boolean hasIpCount) {

        List<ServiceWrapper> matchList;

        //如果找不到命名空间的集合就返回空
        if (chooseServiceMap(namespaceId) == null) {
            return 0;
        }

        //是否
        if (StringUtils.isNotBlank(param)) {
            //分隔符：@@
            StringJoiner regex = new StringJoiner(Constants.SERVICE_INFO_SPLITER);
            //如果s为空，则@@@@，如果不为空则
            for (String s : param.split(Constants.SERVICE_INFO_SPLITER)) {
                regex.add(StringUtils.isBlank(s) ? Constants.ANY_PATTERN : Constants.ANY_PATTERN + s + Constants.ANY_PATTERN);
            }
            matchList = searchServices(namespaceId, regex.toString());
        } else {
            matchList = new ArrayList<>(chooseServiceMap(namespaceId).values());
        }

        if (!CollectionUtils.isEmpty(matchList) && hasIpCount) {
            matchList = matchList.stream().filter(s -> !CollectionUtils.isEmpty(s.allIPs())).collect(Collectors.toList());
        }

        if (StringUtils.isNotBlank(containedInstance)) {

            boolean contained;
            for (int i = 0; i < matchList.size(); i++) {
                ServiceWrapper service = matchList.get(i);
                contained = false;
                List<InstanceWrapper> instances = service.allIPs();
                for (InstanceWrapper instance : instances) {
                    if (containedInstance.contains(":")) {
                        if (StringUtils.equals(instance.getIp() + ":" + instance.getPort(), containedInstance)) {
                            contained = true;
                            break;
                        }
                    } else {
                        if (StringUtils.equals(instance.getIp(), containedInstance)) {
                            contained = true;
                            break;
                        }
                    }
                }
                if (!contained) {
                    matchList.remove(i);
                    i--;
                }
            }
        }

        if (pageSize >= matchList.size()) {
            serviceList.addAll(matchList);
            return matchList.size();
        }

        for (int i = 0; i < matchList.size(); i++) {
            if (i < startPage * pageSize) {
                continue;
            }

            serviceList.add(matchList.get(i));

            if (serviceList.size() >= pageSize) {
                break;
            }
        }

        return matchList.size();
    }

    public Map<String, ServiceWrapper> chooseServiceMap(String namespaceId) {
        return serviceMap.get(namespaceId);
    }

    public List<ServiceWrapper> searchServices(String namespaceId, String regex) {
        List<ServiceWrapper> result = new ArrayList<>();
        for (Map.Entry<String, ServiceWrapper> entry : chooseServiceMap(namespaceId).entrySet()) {
            ServiceWrapper service = entry.getValue();
            String key = service.getName() + ":" + ArrayUtils.toString(service.getLastModifyTime());
            if (key.matches(regex)) {
                result.add(service);
            }
        }

        return result;
    }
}
